﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Moq;
using NUnit.Framework;
using Tycho.Lexer;
using Tycho.Parser.Peg;
using Tycho.Parser.Tokens;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class NonTerminalSpecification {
        [Test]
        public void ReallyParseShouldReturnCompositeTermWithNamedRules () {
            const string termName = "multiply";
            NonTerminal<object> nt = new NonTerminal<object> (termName, null);
            nt.Rule = new SequenceRule<object> (
                new NamedRule<object> ("left", new IdentifierTerminal<object> ()),
                new KeywordRule<object> ("*"),
                new NamedRule<object> ("right", new IdentifierTerminal<object> ()));

            Token [] tokens = NativeLexer.Lex ("a * b").ToArray ();

            var result = nt.Parse (tokens, 0, null);

            var abTerm = result.Term as CompositeTerm;
            Assert.IsNotNull (abTerm);
            Assert.AreEqual (termName, abTerm.Name);

            var aTerm = abTerm.SubTerms ["left"] as IdentifierTerm;
            Assert.IsNotNull (aTerm);
            Assert.AreEqual ("a", aTerm.Name);

            var bTerm = abTerm.SubTerms ["right"] as IdentifierTerm;
            Assert.IsNotNull (bTerm);
            Assert.AreEqual ("b", bTerm.Name);
        }

        [Test]
        public void ReallyParseShouldSwapAssociationIfLeftAssociativeIsSet () {
            var termName = "plus";
            var nt = new NonTerminal<object> (termName, null, null, new AssociationSwapper ("left", "right"));
            var choice = new ChoiceProduction<object> (nt, new IdentifierTerminal<object> ());
            nt.Rule = new SequenceRule<object> (
                new NamedRule<object> ("left", new IdentifierTerminal<object> ()),
                new KeywordRule<object> ("+"),
                new NamedRule<object> ("right", choice));

            Token [] tokens = NativeLexer.Lex ("a + b + c").ToArray ();

            var result = choice.Parse (tokens, 0, null);

            var abcTerm = result.Term as CompositeTerm;
            Assert.IsNotNull (abcTerm);
            Assert.AreEqual (termName, abcTerm.Name);

            var abTerm = abcTerm.SubTerms ["left"] as CompositeTerm;
            Assert.IsNotNull (abTerm);
            Assert.AreEqual (termName, abTerm.Name);

            var aTerm = abTerm.SubTerms ["left"] as IdentifierTerm;
            Assert.IsNotNull (aTerm);
            Assert.AreEqual ("a", aTerm.Name);

            var bTerm = abTerm.SubTerms ["right"] as IdentifierTerm;
            Assert.IsNotNull (bTerm);
            Assert.AreEqual ("b", bTerm.Name);

            var cTerm = abcTerm.SubTerms ["right"] as IdentifierTerm;
            Assert.IsNotNull (cTerm);
            Assert.AreEqual ("c", cTerm.Name);
        }

        [Test]
        public void ShouldAddItemsToListThatAreInMultiplesSet () {
            var nt = new NonTerminal<object> (
                "name",
                new SequenceRule<object> (
                    new NamedRule<object> ("a", new IdentifierTerminal<object> ()),
                    new NamedRule<object> ("a", new IntegerTerminal<object> ())),
                new[] { "a", "b" });

            Token [] tokens = NativeLexer.Lex ("x 9").ToArray ();

            var result = nt.Parse (tokens, 0, null);
            Assert.IsNotNull (result);
            var compositeTerm = result.Term as CompositeTerm;
            Assert.IsNotNull (compositeTerm);
            Assert.AreEqual (2, compositeTerm.SubTerms.Count);
            var listTerm = compositeTerm.SubTerms ["a"] as ListTerm;
            Assert.IsNotNull (listTerm);
            Assert.AreEqual (2, listTerm.Terms.Count);
            Assert.AreEqual ("x", ((IdentifierTerm) listTerm.Terms [0]).Name);
            Assert.AreEqual (9, ((IntegerTerm) listTerm.Terms [1]).Value);

            listTerm = compositeTerm.SubTerms ["b"] as ListTerm;
            Assert.IsNotNull (listTerm);
            Assert.AreEqual (0, listTerm.Terms.Count);
        }
    }
}